﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Reflection;
using System.Data;

namespace TransactionServer.OSActivities
{
    using TransactionServer.General_Classes;

    public class OSConnector : ISystemConnector
    {
        public string connection;
        private dynamic _OSConnection;
        private dynamic v82comConnector;
        private dynamic v82;
        private string connenctionString;

        public OSConnector(Dictionary<string, string> settings) : base(settings) { }

        /// <summary>
        /// Connection to 1C
        /// </summary>
        public dynamic OSConnection
        {
            get
            {
                if (_OSConnection == null)
                {
                    v82comConnector = Type.GetTypeFromProgID("V82.ComConnector");
                    v82 = Activator.CreateInstance(v82comConnector);
                    connenctionString = String.Empty;

                    if (!String.IsNullOrEmpty(settings["cluster"]))
                    {
                        connenctionString = String.Format(@"Srvr=""{0}"";Ref=""{1}"";", settings["cluster"], settings["database"]);
                    }
                    else
                    {
                        connenctionString = String.Format(@"File=""{0}"";", settings["path"]);
                    }

                    connenctionString += String.Format(@"Usr=""{0}"";", settings["user"]);

                    if (!String.IsNullOrEmpty(settings["password"]))
                    {
                        connenctionString += String.Format(@"Pwd=""{0}""", settings["password"]);
                    }

                    Object[] arguments = { connenctionString };

                    _OSConnection = v82comConnector.InvokeMember("Connect", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static, null, v82, arguments);
                }
                return _OSConnection;
            }
            // to delete, test purpose only
            set
            {
                _OSConnection = value;
            }
        }

        /// <summary>
        /// Gets next event from 1C system
        /// </summary>
        /// <returns>Next event if any</returns>
        public override Event getNextEvent()
        {
            /*
             * 	 МенеджерЗаписи = РегистрыСведений.MSDto1C_События.СоздатьМенеджерЗаписи();
  Выборка = РегистрыСведений.MSDto1C_События.Выбрать();
  Пока Выборка.Следующий() Цикл
    МенеджерЗаписи = Выборка.ПолучитьМенеджерЗаписи();      
    МенеджерЗаписи.Прочитать();
	Сообщить(МенеджерЗаписи.Дата);
	Сообщить(МенеджерЗаписи.СистемноеПредставление); 
	Сообщить(МенеджерЗаписи.ТипСобытия); 
	Сообщить(МенеджерЗаписи.ТипОбъекта); 
	Сообщить(МенеджерЗаписи.Событие); 		
    МенеджерЗаписи.Удалить();
  Конеццикла; 

             * */
            var manager = GetObjectProperty(OSConnection, "РегистрыСведений");
            var managerlist = GetObjectProperty(manager, "MSDto1C_События");
            var selection = InvokeObjectMethod(managerlist, "Выбрать");
            object recordmanager;

            var serializer = new XmlSerializer(typeof(Event));

            if ((bool)InvokeObjectMethod(selection, "Следующий"))
            {
                recordmanager = InvokeObjectMethod(selection, "ПолучитьМенеджерЗаписи");
                InvokeObjectMethod(recordmanager, "Прочитать");

                string xml = (string)GetObjectProperty(recordmanager, "Событие");
                StringReader sr = new StringReader(xml);
                Event evnt = (Event)serializer.Deserialize(sr);

                InvokeObjectMethod(recordmanager, "Удалить");

                return evnt;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Gets Entity list
        /// </summary>
        /// <returns>Entity list</returns>
        public override List<string> getEntities()
        {
            List<string> entities = new List<string>();
            string[] types = new string[] { "Справочники", "Документы", "РегистрыСведений", "РегистрыНакопления", "РегистрыРасчета" };

            object Metadata = GetObjectProperty(OSConnection, "Метаданные");
            foreach (string ostype in types)
            {
                object lists = GetObjectProperty(Metadata, ostype);

                int count = (int)InvokeObjectMethod(lists, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object list = InvokeObjectMethod(lists, "Получить", new object[] { i });
                    string entityname = ostype + "." + (string)GetObjectProperty(list, "Имя");
                    entities.Add(entityname);

                    if (ostype == "Документы" || ostype == "Справочники")
                    {
                        object tables = GetObjectProperty(list, "ТабличныеЧасти");
                        int count1 = (int)InvokeObjectMethod(tables, "Количество", null);

                        for (int i1 = 0; i1 < count1; i1++)
                        {
                            object table = InvokeObjectMethod(tables, "Получить", new object[] { i1 });
                            entities.Add("ТабличныеЧасти." + entityname + "." + (string)GetObjectProperty(table, "Имя"));
                        }
                    }
                }
            }

            return entities;
        }

        /// <summary>
        /// Gets entitties attributes
        /// </summary>
        /// <param name="entity">Entity name</param>
        /// <returns>List of attributes</returns>
        public override List<string> getAttributes(string entity)
        {
            string entitytype = entity.Split(new char[] { '.' })[0];
            string entityname = entity.Split(new char[] { '.' })[1];

            string[] attrtypes = new string[] { "Измерения", "Ресурсы", "Реквизиты" };

            object Metadata = GetObjectProperty(OSConnection, "Метаданные");
            object lists = null;
            object list = null;
            object attributes = null;

            List<string> result = new List<string>();

            if (entitytype == "Справочники")
            {

                lists = GetObjectProperty(Metadata, entitytype);
                list = GetObjectProperty(lists, entityname);
                attributes = GetObjectProperty(list, "Реквизиты");


                int count = (int)InvokeObjectMethod(attributes, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                    result.Add((string)GetObjectProperty(attribute, "Имя"));
                }

                result.Add("Код");
                result.Add("Наименование");
                result.Add("Родитель");
                result.Add("Владелец");
                /*attributes = GetObjectProperty(list, "СтандартныеРеквизиты");

                count = (int)InvokeObjectMethod(attributes, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                    result.Add((string)GetObjectProperty(attribute, "Имя"));
                }*/

            }
            else if (entitytype == "Документы")
            {
                lists = GetObjectProperty(Metadata, entitytype);
                list = GetObjectProperty(lists, entityname);
                attributes = GetObjectProperty(list, "Реквизиты");


                int count = (int)InvokeObjectMethod(attributes, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                    result.Add((string)GetObjectProperty(attribute, "Имя"));
                }

                result.Add("Номер");
                result.Add("Дата");

                /*attributes = GetObjectProperty(list, "СтандартныеРеквизиты");

                count = (int)InvokeObjectMethod(attributes, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                    result.Add((string)GetObjectProperty(attribute, "Имя"));
                }*/

            }
            else if (entitytype == "РегистрыСведений")
            {

                lists = GetObjectProperty(Metadata, entitytype);
                list = GetObjectProperty(lists, entityname);

                result.Add("Период");

                foreach (string attrtype in attrtypes)
                {
                    attributes = GetObjectProperty(list, attrtype);

                    int count = (int)InvokeObjectMethod(attributes, "Количество", null);

                    for (int i = 0; i < count; i++)
                    {
                        object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                        result.Add(attrtype + "." + (string)GetObjectProperty(attribute, "Имя"));
                    }
                }

            }
            else if (entitytype == "РегистрыНакопления")
            {
                lists = GetObjectProperty(Metadata, entitytype);
                list = GetObjectProperty(lists, entityname);

                foreach (string attrtype in attrtypes)
                {
                    attributes = GetObjectProperty(list, attrtype);

                    int count = (int)InvokeObjectMethod(attributes, "Количество", null);

                    for (int i = 0; i < count; i++)
                    {
                        object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                        result.Add(attrtype + "." + (string)GetObjectProperty(attribute, "Имя"));
                    }
                }
            }
            else if (entitytype == "ТабличныеЧасти")
            {
                string baseentitytype = entity.Split(new char[] { '.' })[1];
                string baseentityname = entity.Split(new char[] { '.' })[2];
                entityname = entity.Split(new char[] { '.' })[3];

                lists = GetObjectProperty(Metadata, baseentitytype);
                list = GetObjectProperty(lists, baseentityname);
                var tables = GetObjectProperty(list, "ТабличныеЧасти");
                var table = GetObjectProperty(tables, entityname);

                attributes = GetObjectProperty(table, "Реквизиты");

                int count = (int)InvokeObjectMethod(attributes, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                    result.Add((string)GetObjectProperty(attribute, "Имя"));
                }

                result.Add(baseentitytype);
                /*attributes = GetObjectProperty(table, "СтандартныеРеквизиты");

                count = (int)InvokeObjectMethod(attributes, "Количество", null);

                for (int i = 0; i < count; i++)
                {
                    object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                    result.Add((string)GetObjectProperty(attribute, "Имя"));
                }*/

            }

            else if (entitytype == "РегистрыРасчета")
            {
                lists = GetObjectProperty(Metadata, entitytype);
                list = GetObjectProperty(lists, entityname);

                foreach (string attrtype in attrtypes)
                {
                    attributes = GetObjectProperty(list, attrtype);

                    int count = (int)InvokeObjectMethod(attributes, "Количество", null);

                    for (int i = 0; i < count; i++)
                    {
                        object attribute = InvokeObjectMethod(attributes, "Получить", new object[] { i });
                        result.Add(attrtype + "." + (string)GetObjectProperty(attribute, "Имя"));
                    }
                }
            }

            /*Для Каждого Реквизит из Документ.Метаданные().ТабличныеЧасти[ИмяТабличнойЧасти].Реквизиты Цикл 

СтруктураТабличнойЧастиДокумента.Вставить(Реквизит.Имя, Реквизит.Имя); 

КонецЦикла; */

            return result;
        }

        /// <summary>
        /// Converts Entity to 1C COM object
        /// </summary>
        /// <param name="ent">Name of entity</param>
        /// <returns>COM object</returns>
        public object ConvertFrom(Entity ent)
        {
            string entitytype = ent.Name.Split(new char[] { '.' })[0];
            string entityname = String.Empty;
            string primaryentitytype = ent.Name.Split(new char[] { '.' })[1];
            object primaryentity = null;

            object Metadata = null;
            object lists = null;
            object list = null;

            if (entitytype == "ТабличныеЧасти")
            {
                entityname = ent.Name.Split(new char[] { '.' })[2];

                Metadata = GetObjectProperty(OSConnection, "Метаданные");
                lists = GetObjectProperty(Metadata, primaryentitytype);
                list = GetObjectProperty(lists, entityname);
            }
            else
            {
                entityname = ent.Name.Split(new char[] { '.' })[1];

                Metadata = GetObjectProperty(OSConnection, "Метаданные");
                lists = GetObjectProperty(Metadata, entitytype);
                list = GetObjectProperty(lists, entityname);
            }

            object manager = null;
            string subentityname = "";
            string createcommand = "";
            object entity = null;



            if (entitytype == "Справочники")
            {
                manager = GetObjectProperty(OSConnection, "Справочники");
                createcommand = "СоздатьЭлемент";
            }
            else if (entitytype == "Документы")
            {
                manager = GetObjectProperty(OSConnection, "Документы");
                createcommand = "СоздатьДокумент";
            }
            else if (entitytype == "ТабличныеЧасти")
            {
                manager = GetObjectProperty(OSConnection, primaryentitytype);
                subentityname = ent.Name.Split(new char[] { '.' })[3];
                list = GetObjectProperty(list, "ТабличныеЧасти");
                list = GetObjectProperty(list, subentityname);
                createcommand = "Добавить";
            }
            else if (entitytype == "РегистрыСведений")
            {
                manager = GetObjectProperty(OSConnection, "РегистрыСведений");
                createcommand = "СоздатьМенеджерЗаписи";
            }

            object managerlist = GetObjectProperty(manager, entityname);

            if (entitytype == "ТабличныеЧасти")
            {
                if (ent.ContainsKey("Документы"))
                {
                    managerlist = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { ent["Документы"] });
                    managerlist = InvokeObjectMethod(managerlist, "ПолучитьОбъект", null);
                    primaryentity = managerlist;
                    managerlist = GetObjectProperty(primaryentity, subentityname);
                }
                else if (ent.ContainsKey("Справочники"))
                {
                    managerlist = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { ent["Справочники"] });
                    managerlist = InvokeObjectMethod(managerlist, "ПолучитьОбъект", null);
                    primaryentity = managerlist;
                    managerlist = GetObjectProperty(primaryentity, subentityname);
                }
                else
                {
                    return null;
                }
            }

            if (String.IsNullOrEmpty(ent.Id))
            {
                entity = InvokeObjectMethod(managerlist, createcommand, new Object[] { });
            }
            else
            {
                if (entitytype == "ТабличныеЧасти")
                {
                    entity = InvokeObjectMethod(managerlist, "Получить", new object[] { ent.Id.Split('/')[1] });
                }
                else
                {
                    entity = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { ent.Id });
                    entity = InvokeObjectMethod(entity, "ПолучитьОбъект", null);
                }
            }



            foreach (KeyValuePair<string, object> attr in ent)
            {
                object recvs = null;
                string attrName = String.Empty;

                if (entitytype == "Справочники" || entitytype == "Документы" || entitytype == "ТабличныеЧасти")
                {
                    recvs = GetObjectProperty(list, "Реквизиты");
                    attrName = attr.Key;
                }
                else if (entitytype == "РегистрыСведений")
                {
                    if (attr.Key == "Период")
                    {
                        recvs = GetObjectProperty(list, "Измерения");
                        attrName = attr.Key;
                    }
                    else
                    {
                        recvs = GetObjectProperty(list, attr.Key.Split('.')[0]);
                        attrName = attr.Key.Split('.')[1];
                    }
                }
                else
                {
                    throw new Exception("Entity not supported!");
                }

                var recv = InvokeObjectMethod(recvs, "Найти", new Object[] { attrName });

                /* if (attrName == "Тип")
                 {
                     var listi = GetObjectProperty(OSConnection, "Перечисления");
                     var list1 = GetObjectProperty(listi, "ТипыКонтактнойИнформации");
                     var val = GetObjectProperty(list1, "Телефон");

                     SetObjectProperty(entity, attrName, val);
                 }
                 else */
                if (recv == null)
                {
                    if (attr.Key == "Наименование")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else if (attr.Key == "Дата")
                    {
                        SetObjectProperty(entity, attrName, DateTime.Parse((string)attr.Value).ToString("yyyyMMddhhmmss"));
                    }
                    else if (attr.Key == "Период")
                    {
                        SetObjectProperty(entity, attrName, DateTime.Parse((string)attr.Value).ToString("yyyyMMddhhmmss"));
                    }
                    else if (attr.Key == "Номер")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else if (attr.Key == "Документ")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else if (attr.Key == "Код")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else if (attr.Key == "Родитель")
                    {
                        SetObjectProperty(entity, attrName, InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { attr.Value }));
                    }
                    else if (attr.Key == "Владелец")
                    {
                        SetObjectProperty(entity, attrName, InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { attr.Value }));
                    }
                }
                else
                {
                    object types = GetObjectProperty(recv, "Тип");
                    object typ = InvokeObjectMethod(types, "Типы", new object[] { });
                    object typing = InvokeObjectMethod(typ, "Получить", new object[] { 0 });
                    string num = (string)InvokeObjectMethod(OSConnection, "String", new object[] { typing });

                    if (num == "Строка" || num == "String")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else if (num == "Число" || num == "Number")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else if (num == "Дата" || num == "Date")
                    {
                        SetObjectProperty(entity, attrName, DateTime.Parse((string)attr.Value).ToString("yyyyMMddhhmmss"));
                    }
                    else if (num == "Булево" || num == "Boolean")
                    {
                        SetObjectProperty(entity, attrName, attr.Value);
                    }
                    else
                    {
                        if (attr.Value is String)
                        {
                            if (!String.IsNullOrEmpty(attr.Value.ToString()))
                            {
                                SetObjectProperty(entity, attrName, InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { attr.Value }));
                            }
                        }
                        else if (attr.Value != null)
                        {
                            SetObjectProperty(entity, attrName, attr.Value);
                        }
                    }

                }
            }

            if (entitytype == "ТабличныеЧасти")
            {
                return primaryentity;
            }
            else
            {
                return entity;
            }
        }

        /// <summary>
        /// Gets number of rows in table part
        /// </summary>
        /// <param name="entity">COM object</param>
        /// <param name="tablename">Name of table part</param>
        /// <returns>Count.ToString()</returns>
        public string getRowCount(object entity, string tablename)
        {
            var table = GetObjectProperty(entity, tablename);
            int count = (int)InvokeObjectMethod(table, "Количество", null) - 1;
            return count.ToString();
        }

        /// <summary>
        /// Gets list (перечисление) COM object by its string representation
        /// </summary>
        /// <param name="list">List name</param>
        /// <param name="value">List representation</param>
        /// <returns>COM object</returns>
        public object lookupListByValue(string list, string value)
        {
            var listi = GetObjectProperty(OSConnection, "Перечисления");
            var list1 = GetObjectProperty(listi, list);
            var val = GetObjectProperty(list1, value);

            return val;
        }

        /// <summary>
        /// Gets list (перечисление) name by Id
        /// </summary>
        /// <param name="id">List Id</param>
        /// <returns>List name</returns>
        public object getListName(string id)
        {
            var obj = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { id });

            // ИмяПеречисления = ЗначениеПеречисления.Метаданные().Имя;
            var enummetadata = InvokeObjectMethod(obj, "Метаданные");
            string enumname = (string)GetObjectProperty(enummetadata, "Имя");

            //ИндексЗначенияПеречисления = Перечисления[ИмяПеречисления].Индекс(ЗначениеПеречисления);
            var enums = GetObjectProperty(OSConnection, "Перечисления");
            var enum1c = GetObjectProperty(enums, enumname);
            var enumindex = (int)InvokeObjectMethod(enum1c, "Индекс", new object[] { obj });

            //ИмяЗначенияПеречисления = Метаданные.Перечисления[ИмяПеречисления].ЗначенияПеречисления[ИндексЗначенияПеречисления].Имя;
            var metadata = GetObjectProperty(OSConnection, "Метаданные");
            enums = GetObjectProperty(metadata, "Перечисления");
            enum1c = GetObjectProperty(enums, enumname);
            var enumvalues = GetObjectProperty(enum1c, "ЗначенияПеречисления");
            var enumvalue = InvokeObjectMethod(enumvalues, "Получить", new object[] { enumindex });

            var val = GetObjectProperty(enumvalue, "Имя");

            return val;
        }

        /// <summary>
        /// Gets field value by entity Id
        /// </summary>
        /// <param name="id">Id of entity</param>
        /// <param name="field">Field name</param>
        /// <returns>Field value</returns>
        public string lookupFieldById(string id, string field)
        {
            var obj = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { id }); ;

            object value = GetObjectProperty(obj, field);
            if (value is string || value is int || value is decimal || value is DateTime)
            {
                value = value.ToString();
            }
            else
            {
                value = (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { value });
            }

            return value.ToString();
        }

        /// <summary>
        /// Returns fields of specified entity
        /// </summary>
        /// <param name="ent">Entity containing Id</param>
        /// <param name="fields">Fields list to return</param>
        public void lookupFieldsById(Entity ent, string[] fields)
        {
            var obj = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { ent.Id });

            foreach (var field in fields)
            {
                object value = GetObjectProperty(obj, field);
                if (value is string || value is int || value is decimal || value is DateTime)
                {
                    ent.Add(field, value.ToString());
                }
                else
                {
                    ent.Add(field, (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { value }));
                }
            }
        }

        /// <summary>
        /// Searchs dictionary element by field value
        /// </summary>
        /// <param name="dictionary">Name of dictionary</param>
        /// <param name="field">Field name</param>
        /// <param name="value">Field value</param>
        /// <returns>Id of founded object</returns>
        public string lookupDictionaryByField(string dictionary, string field, string value)
        {
            var manager = GetObjectProperty(OSConnection, "Справочники");
            var managerlist = GetObjectProperty(manager, dictionary);
            object result = null;

            if (field == "Код")
            {
                result = InvokeObjectMethod(managerlist, "НайтиПоКоду", new object[] { value });
            }
            else if (field == "Наименование")
            {
                result = InvokeObjectMethod(managerlist, "НайтиПоНаименованию", new object[] { value });
            }
            else
            {
                result = InvokeObjectMethod(managerlist, "НайтиПоРеквизиту", new object[] { field, value });
            }

            return (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { GetObjectProperty(result, "Ссылка") });
        }

        /// <summary>
        /// Ger list of objects by entity name
        /// </summary>
        /// <param name="entityName">Name of entity - for example "Справочники.Контрагенты"</param>
        /// <returns>Table of found objects</returns>
        public DataTable getObjectList(string entityName)
        {
            var result = new DataTable();
            result.Columns.Add("objectid");
            result.Columns.Add("name");

            string entitytype = entityName.Split(new char[] { '.' })[0];
            object manager = null;
            string entityname = String.Empty;
            string nameField = String.Empty;

            if (entitytype == "Справочники")
            {
                manager = GetObjectProperty(OSConnection, "Справочники");
                entityname = entityName.Split(new char[] { '.' })[1];
                nameField = "Наименование";
            }
            else if (entitytype == "Документы")
            {
                manager = GetObjectProperty(OSConnection, "Документы");
                entityname = entityName.Split(new char[] { '.' })[1];
                nameField = "Номер";
            }
            else
            {
                throw new Exception("1C Object not supported");
            }

            var managerlist = GetObjectProperty(manager, entityname);

            /*ВыборкаСправочника = Справочники.Валюты.Выбрать();
Пока ВыборкаСправочника.Следующий() Цикл
    Сообщить("Записана валюта " + ВыборкаСправочника.Наименование);
КонецЦикла;*/

            var selection = InvokeObjectMethod(managerlist, "Выбрать");
            while ((bool)InvokeObjectMethod(selection, "Следующий"))
            {
                var row = result.NewRow();
                row["objectid"] = InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { GetObjectProperty(selection, "Ссылка") });
                row["name"] = GetObjectProperty(selection, nameField);
                result.Rows.Add(row);
            }

            return result;
        }

        /// <summary>
        /// Markes entity as deleted or removes string from document
        /// </summary>
        /// <param name="entity">Object to delete</param>
        public void Delete(Entity entity)
        {
            string entitytype = entity.Name.Split('.')[0];
            object osEntity = null;

            if (!String.IsNullOrEmpty(entity.Id))
            {
                if (entitytype == "ТабличныеЧасти")
                {
                    string entityname = entity.Name.Split('.')[3];

                    osEntity = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { entity.Id.Split('/')[0] });
                    osEntity = InvokeObjectMethod(osEntity, "ПолучитьОбъект", null);
                    var table = GetObjectProperty(osEntity, entityname);
                    InvokeObjectMethod(table, "Удалить", new object[] { entity.Id.Split('/')[1] });
                    SaveEntity(osEntity);
                }
                else
                {
                    osEntity = InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { entity.Id });
                    osEntity = InvokeObjectMethod(osEntity, "ПолучитьОбъект", null);
                    SetObjectProperty(osEntity, "ПометкаУдаления", true);
                    SaveEntity(osEntity);
                }
            }
        }

        /// <summary>
        /// Moves 1C document
        /// </summary>
        /// <param name="entity">1C document entity represented by COM object</param>
        public void MoveDocument(object entity)
        {
            var writeMothod = GetObjectProperty(OSConnection, "РежимЗаписиДокумента");
            var move = GetObjectProperty(writeMothod, "Проведение");

            InvokeObjectMethod(entity, "Записать", new Object[] { move });
            
            string id = String.Empty;

            id = (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { GetObjectProperty(entity, "Ссылка") });
        }

        /// <summary>
        /// Declines document move
        /// </summary>
        /// <param name="entity">1C document represented by COM object</param>
        public void UnMoveDocument(object entity)
        {
            var writeMothod = GetObjectProperty(OSConnection, "РежимЗаписиДокумента");
            var move = GetObjectProperty(writeMothod, "ОтменаПроведения");

            InvokeObjectMethod(entity, "Записать", new Object[] { move });

            string id = String.Empty;

            id = (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { GetObjectProperty(entity, "Ссылка") });
        }

        /// <summary>
        /// Stores 1C entity calling Save function
        /// </summary>
        /// <param name="entity">COM object, representing 1C object</param>
        /// <returns></returns>
        public string SaveEntity(object entity)
        {
            try
            {
                InvokeObjectMethod(entity, "Записать", new Object[] { true });
            }
            catch
            {
                InvokeObjectMethod(entity, "Записать", null);
            }
            string id = String.Empty;

            try
            {
                id = (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new Object[] { GetObjectProperty(entity, "Ссылка") });
            }
            catch { };
            return id;
        }

        /// <summary>
        /// Gets type of attribute represented by COM object of Metadata type
        /// </summary>
        /// <param name="recv">Attribute COM object</param>
        /// <returns></returns>
        private string getType(object recv)
        {
            object types = GetObjectProperty(recv, "Тип");
            object typ = InvokeObjectMethod(types, "Типы", new object[] { });
            object typing = InvokeObjectMethod(typ, "Получить", new object[] { 0 });
            return (string)InvokeObjectMethod(OSConnection, "String", new object[] { typing });
        }

        /// <summary>
        /// Gets type of attribute
        /// </summary>
        /// <param name="entitytype">Type of entity, for example "Справочник"</param>
        /// <param name="entityname">Name of entity, for example "Контрагент"</param>
        /// <param name="attribute">Name of attribute</param>
        /// <returns></returns>
        private string getAttributeType(string entitytype, string entityname, string attribute)
        {
            var Metadata = GetObjectProperty(OSConnection, "Метаданные");
            object metadata = null;
            if (entitytype == "ТабличныеЧасти")
            {
                metadata = GetObjectProperty(Metadata, entityname.Split(new char[] { '.' })[0]);
                metadata = GetObjectProperty(metadata, entityname.Split(new char[] { '.' })[1]);
                metadata = GetObjectProperty(metadata, "ТабличныеЧасти");
                metadata = GetObjectProperty(metadata, entityname.Split(new char[] { '.' })[2]);
            }
            else
            {
                metadata = GetObjectProperty(Metadata, entitytype);
                metadata = GetObjectProperty(metadata, entityname);
            }
            string result = String.Empty;

            object recvs = null;
            object recv = null;

            switch (entitytype)
            {
                case "ТабличныеЧасти":
                    recvs = GetObjectProperty(metadata, "Реквизиты");
                    recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                    result = getType(recv);
                    break;
                case "Справочники":
                    try
                    {
                        try
                        {
                            recvs = GetObjectProperty(metadata, "СтандартныеРеквизиты");
                            recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                            result = getType(recv);
                        }
                        catch
                        {
                            recvs = GetObjectProperty(metadata, "Реквизиты");
                            recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                            result = getType(recv);
                        }
                    }
                    catch
                    {
                        if (attribute == "Родитель")
                        {
                            result = "Справочник";
                        }
                        else
                        {
                            result = "Строка";
                        }
                    }
                    break;
                case "Документы":
                    try
                    {
                        try
                        {
                            recvs = GetObjectProperty(metadata, "СтандартныеРеквизиты");
                            recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                            result = getType(recv);
                        }
                        catch
                        {
                            recvs = GetObjectProperty(metadata, "Реквизиты");
                            recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                            result = getType(recv);
                        }
                    }
                    catch { result = "Строка"; }
                    break;
                case "РегистрыСведений":
                    try
                    {
                        try
                        {
                            recvs = GetObjectProperty(metadata, "Измерения");
                            recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                            result = getType(recv);
                        }
                        catch
                        {
                            recvs = GetObjectProperty(metadata, "Ресурсы");
                            recv = InvokeObjectMethod(recvs, "Найти", new object[] { attribute });
                            result = getType(recv);
                        }
                    }
                    catch { result = "Строка"; }
                    break;
                default:
                    break;
            }

            return result;
        }

        /// <summary>
        /// Get 1C objects by fiter
        /// </summary>
        /// <param name="filter">Filter contains info about criterias and attributes list to return</param>
        /// <returns></returns>
        public List<Entity> getObjects(Filter filter)
        {
            List<Entity> result = new List<Entity>();

            string entitytype = filter.entity.Split(new char[] { '.' })[0];
            string entityname = filter.entity.Split(new char[] { '.' })[1];
            string parententitytype = "";
            string tablename = String.Empty;

            switch (entitytype)
            {
                case "РегистрыСведений":
                    tablename = "РегистрСведений.";
                    break;
                case "РегистрыНакопления":
                    tablename = "РегистрНакопления.";
                    break;
                case "Справочники":
                    tablename = "Справочник.";
                    break;
                case "Документы":
                    tablename = "Документ.";
                    break;
                case "ТабличныеЧасти":
                    switch (entityname)
                    {
                        case "Справочники":
                            tablename = "Справочник.";
                            break;
                        case "Документы":
                            tablename = "Документ.";
                            break;
                        default:
                            break;
                    }
                    parententitytype = entityname + ".";
                    entityname = filter.entity.Split(new char[] { '.' })[2] + "." + filter.entity.Split(new char[] { '.' })[3];
                    break;
                default:
                    break;
            }
            tablename += entityname;

            StringBuilder queryText = new StringBuilder("ВЫБРАТЬ ");

            bool coma = false;
            if (filter.attributes.Count > 0)
            {
                foreach (CAttribute attribute in filter.attributes)
                {
                    if (coma)
                    {
                        queryText.Append(",");
                    }
                    if (attribute.name == "Количество")
                    {
                        queryText.Append(attribute.name + " КАК Количество_ВременнаяПеременнаяИззаПрикола1С");
                    }
                    else
                    {
                        queryText.Append(attribute.name);
                    }
                    coma = true;
                }
                if (entitytype != "РегистрыСведений" && entitytype != "РегистрыНакопления")
                {
                    queryText.Append("," + tablename + ".Ссылка КАК Ссылка");

                    if (entitytype == "ТабличныеЧасти")
                    {
                        queryText.AppendFormat(", {0}.НомерСтроки", tablename);
                    }
                }
            }
            else
            {
                if (entitytype != "РегистрыСведений" && entitytype != "РегистрыНакопления" && entitytype != "ТабличныеЧасти")
                {
                    queryText.Append(tablename + ".Ссылка КАК Ссылка");

                    if (entitytype == "ТабличныеЧасти")
                    {
                        queryText.AppendFormat(", {0}.НомерСтроки", tablename);
                    }
                }
                else
                {
                    queryText.Append("*");
                }
            }

            queryText.AppendFormat(" ИЗ {0}", tablename);

            if (filter.conditions.Count > 0)
            {
                queryText.Append(" ГДЕ ");
                bool and = false;
                foreach (CCondition condition in filter.conditions)
                {
                    if (and)
                    {
                        queryText.Append(" И ");
                    }
                    queryText.AppendFormat("{0} {1} &{0}", condition.attribute, condition.opr);

                    and = true;
                }
            }

            var request = InvokeObjectMethod(OSConnection, "NewObject", new object[] { "Запрос" });
            SetObjectProperty(request, "Текст", queryText.ToString());

            foreach (CCondition condition in filter.conditions)
            {
                string num = String.Empty;
                if (entitytype == "ТабличныеЧасти")
                {
                    if (condition.attribute == "Ссылка")
                    {
                        num = "Ссылка";
                    }
                    else
                    {
                        num = getAttributeType(entitytype, parententitytype + entityname, condition.attribute);
                    }
                }
                else
                {
                    num = getAttributeType(entitytype, entityname, condition.attribute);
                }

                    //Запрос.УстановитьПараметр("Владелец", Контрагент); 
                if (num == "Строка" || num == "String")
                {
                    InvokeObjectMethod(request, "УстановитьПараметр", new object[] { condition.attribute, condition.value });
                }
                else if (num == "Число" || num == "Number")
                {
                    InvokeObjectMethod(request, "УстановитьПараметр", new object[] { condition.attribute, Decimal.Parse(condition.value) });
                }
                else if (num == "Дата" || num == "Date")
                {
                    InvokeObjectMethod(request, "УстановитьПараметр", new object[] { condition.attribute, condition.value });
                }
                else if (num == "Булево" || num == "Boolean")
                {
                    InvokeObjectMethod(request, "УстановитьПараметр", new object[] { condition.attribute, condition.value });
                }
                else
                {
                    InvokeObjectMethod(request, "УстановитьПараметр", new object[] { condition.attribute, InvokeObjectMethod(OSConnection, "ЗначениеИзСтрокиВнутр", new object[] { condition.value }) });
                }

            }

            var reqResult = InvokeObjectMethod(request, "Выполнить");

            var requestGoThrowConst = GetObjectProperty(OSConnection, "ОбходРезультатаЗапроса");
            var groupConst = GetObjectProperty(requestGoThrowConst, "ПоГруппировкам");
            var directConst = GetObjectProperty(requestGoThrowConst, "Прямой");

            var select = InvokeObjectMethod(reqResult, "Выбрать", new object[] { directConst });
            //select = InvokeObjectMethod(select, "Выбрать", new object[] { directConst });

            var columns = GetObjectProperty(reqResult, "Колонки");
            int colcount = (int)InvokeObjectMethod(columns, "Количество");


            while ((bool)InvokeObjectMethod(select, "Следующий"))
            {
                Entity ent = new Entity()
                {
                    Name = filter.entity,
                    System = filter.connection
                };

                int strNumber = -1;

                for (int i = 0; i < colcount; i++)
                {
                    var column = InvokeObjectMethod(columns, "Получить", new object[] { i });
                    string columnName = (string)GetObjectProperty(column, "Имя");                  

                    if (columnName == "Ссылка")
                    {
                        if (strNumber > -1)
                        {
                            ent.Id = String.Format("{0}/{1}",
                                (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new object[] { GetObjectProperty(select, columnName) }),
                                strNumber - 1);
                        }
                        else
                        {
                            ent.Id = (string)InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new object[] { GetObjectProperty(select, columnName) });
                        }
                    }
                    else if (columnName == "НомерСтроки")
                    {
                        if (!String.IsNullOrEmpty(ent.Id))
                        {
                            strNumber = (int)GetObjectProperty(select, columnName);
                            ent.Id += "/" + (strNumber - 1).ToString();
                        }
                        else
                        {
                            strNumber = (int)GetObjectProperty(select, columnName);
                        }
                    }
                    else
                    {
                        string num = String.Empty;
                        if (entitytype == "ТабличныеЧасти")
                        {
                            num = getAttributeType(entitytype, parententitytype + entityname, columnName == "Количество_ВременнаяПеременнаяИззаПрикола1С" ? "Количество" : columnName);
                        }
                        else
                        {
                            num = getAttributeType(entitytype, entityname, columnName);
                        }

                        //Запрос.УстановитьПараметр("Владелец", Контрагент); 
                        if (num == "Строка" || num == "String")
                        {
                            ent.Add(columnName, GetObjectProperty(select, columnName).ToString());
                        }
                        else if (num == "Число" || num == "Number")
                        {
                            // Этот жесткий хардкод вызван тем, что Количество зарезервированное слово в переменной Запрос.
                            if (columnName == "Количество_ВременнаяПеременнаяИззаПрикола1С")
                            {
                                ent.Add("Количество", GetObjectProperty(select, columnName).ToString());
                            }
                            else
                            {
                                ent.Add(columnName, GetObjectProperty(select, columnName).ToString());
                            }
                        }
                        else if (num == "Дата" || num == "Date")
                        {
                            ent.Add(columnName, GetObjectProperty(select, columnName).ToString());
                        }
                        else if (num == "Булево" || num == "Boolean")
                        {
                            ent.Add(columnName, GetObjectProperty(select, columnName).ToString());
                        }
                        else
                        {
                            ent.Add(columnName, InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new object[] { GetObjectProperty(select, columnName) }).ToString());
                        }

                        if (ent.ContainsKey("type"))
                        {
                            ent["type"] = num;
                        }
                        else
                        {
                            ent.Add("type", num);
                        }
                    }
                }

                result.Add(ent);
            }

            /*РезультатЗапроса = Запрос.Выполнить();
ТабДок.НачатьАвтогруппировкуСтрок();
ВыборкаИтоги = 
    РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
ВыборкаИтоги.Следующий();
Выборка = ВыборкаИтоги.Выбрать(ОбходРезультатаЗапроса.Прямой);
Пока Выборка.Следующий() Цикл
    Область = Макет.ПолучитьОбласть("Строка");
    // заполнение параметров строки отчета
    Область.Параметры.Номенклатура = 
        Выборка.НоменклатураПредставление;
    Область.Параметры.Расшифровка = Выборка.Номенклатура;
    Область.Параметры.НачОстаток = Выборка.НачОстаток;
    Область.Параметры.Приход = Выборка.Приход;
    Область.Параметры.Расход = Выборка.Расход;
    Область.Параметры.КонОстаток = Выборка.КонОстаток;
    ТабДок.Вывести(Область, Выборка.Уровень());
КонецЦикла;
ТабДок.ЗакончитьАвтогруппировкуСтрок*/

            return result;
        }

        #region "COM object extensions"

        public object SetObjectProperty(object refObject, string propertyName, object value)
        {
            /*try
            {*/
            object[] arg = new object[] { value };
            return refObject.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, refObject, arg);
            /*}
            catch (Exception ex)
            {
                string s = (string)InvokeObjectMethod(OSConnection, "ОписаниеОшибки");
                throw new Exception(s);
            }*/
        }

        public object GetObjectProperty(object refObject, string propertyName, object[] args)
        {
            /*try
            {*/
            return refObject.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, refObject, args);
            /*}
            catch (Exception ex)
            {
                string s = (string)InvokeObjectMethod(OSConnection, "ОписаниеОшибки");
                throw new Exception(s);
            }*/
        }

        public object GetObjectProperty(object v82, object refObject, string propertyName)
        {
            /*try
            {*/
            return v82.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, refObject, null);
            /*}
            catch (Exception ex)
            {
                string s = (string)InvokeObjectMethod(OSConnection, "ОписаниеОшибки");
                throw new Exception(s);
            }*/
        }

        public object GetObjectProperty(object refObject, string propertyName)
        {
            /*try
            {*/
            return refObject.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, refObject, null);
            /*}
            catch (Exception ex)
            {
                string s = (string)InvokeObjectMethod(OSConnection, "ОписаниеОшибки");
                throw new Exception(s);
            }*/
        }

        public object InvokeObjectMethod(object v82, object refObject, string methodName, Object[] parameters)
        {
            return v82.GetType().InvokeMember(methodName, BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static, null, refObject, parameters);
        }

        public object InvokeObjectMethod(object refObject, string methodName, Object[] parameters)
        {
            return refObject.GetType().InvokeMember(methodName, BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static, null, refObject, parameters);
        }

        public object InvokeObjectMethod(object refObject, string methodName)
        {
            return refObject.GetType().InvokeMember(methodName, BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Static, null, refObject, null);
        }

        #endregion

        /// <summary>
        /// Checks if 1C COM object is available
        /// </summary>
        /// <returns>true if available</returns>
        public override HealthInfo CheckHealth()
        {
            var healthInfo = new HealthInfo()
            {
                ServiceName = GetType().FullName,
            };

            try
            {
                var manager = GetObjectProperty(OSConnection, "РегистрыСведений");
                healthInfo.Status = true;
            }
            catch (Exception ex)
            {
                close();
                healthInfo.Status = false;
                healthInfo.ErrorInfo = ex.Message;
            }

            return healthInfo;
        }

        /// <summary>
        /// Gets "РегистрыСведений.КонтактнаяИнформация", made for simplicity, because this registry is often used
        /// </summary>
        /// <param name="objectId">Parent object Id, for example Contragent Id</param>
        /// <param name="type">Type of contact info</param>
        /// <param name="kind">Code of kind of contact info</param>
        /// <param name="attributes">Attributes to return</param>
        /// <returns></returns>
        public Entity getContactInfo(string objectId, string type, string kind, string[] attributes)
        {
            var result = new Entity(connection, "РегистрыСведений.КонтактнаяИнформация");

            object registerSelection = OSConnection.РегистрыСведений.КонтактнаяИнформация.СоздатьНаборЗаписей();
            dynamic select = GetObjectProperty(registerSelection, "Отбор");
            select.Тип.Значение = lookupListByValue("ТипыКонтактнойИнформации", type);
            select.Тип.Использование = true;
            select.Вид.Значение = OSConnection.ЗначениеИзСтрокиВнутр(lookupDictionaryByField("ВидыКонтактнойИнформации", "Код", kind));
            select.Вид.Использование = true;
            select.Объект.Значение = OSConnection.ЗначениеИзСтрокиВнутр(objectId);
            select.Объект.Использование = true;
            InvokeObjectMethod(registerSelection, "Прочитать");
            object selection = null;

            try
            {
                selection = InvokeObjectMethod(registerSelection, "Получить", new object[] { 0 });
            }
            catch (Exception ex)
            {
                result = null;
            }

            foreach (var attribute in attributes)
            {
                result.Add(attribute, getEntityAttributeValue(selection, "РегистрыСведений", "КонтактнаяИнформация", attribute));
            }

            return result;
        }

        /// <summary>
        /// Closes connection to 1C COM Object
        /// </summary>
        public override void close()
        {
            _OSConnection = null;
            GC.Collect();
        }

        /// <summary>
        /// Gets value of entity attribute based on COM Object
        /// </summary>
        /// <param name="entity">COM object containing 1C object</param>
        /// <param name="entityType">Type of Entity, for exmple "Справочники" or "ТабличныеЧасти"</param>
        /// <param name="emtityName">Name of entity, for example "Контрагенты"</param>
        /// <param name="columnName">Name of field</param>
        /// <returns></returns>
        public string getEntityAttributeValue(object entity, string entityType, string entityName, string columnName)
        {
            try
            {
                string num = getAttributeType(entityType, entityName, columnName);

                string result;

                //Запрос.УстановитьПараметр("Владелец", Контрагент); 
                if (num == "Строка" || num == "String" || num == "Число" || num == "Number" || num == "Дата" || num == "Date" || num == "Булево" || num == "Boolean")
                {
                    result = GetObjectProperty(entity, columnName).ToString();
                }
                else
                {
                    result = InvokeObjectMethod(OSConnection, "ЗначениеВСтрокуВнутр", new object[] { GetObjectProperty(entity, columnName) }).ToString();
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format("Error getting value of attribute {0} : {1}", columnName, ex.Message));
            }

        }
    }
}
